package com.wiscamp.ninechapters.panels.domain.services;

import com.wiscamp.ninechapters.panels.domain.entities.*;
import com.wiscamp.ninechapters.panels.domain.repositories.*;
import cube.common.cache.CacheManagerNames;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.domain.DomainService;
import cube.ddd.exception.ExceptionUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

@Service
public class UserService implements DomainService {
    private final UserRepository userRepository;

    private final MenuRepository menuRepository;

    private final PermissionRepository permissionRepository;

    private final GroupRepository groupRepository;

    private final RoleRepository roleRepository;

    private final String cacheName = "users";

    private final String defaultPassword = "888888";

    public UserService(UserRepository userRepository, MenuRepository menuRepository, PermissionRepository permissionRepository, GroupRepository groupRepository, RoleRepository roleRepository) {
        this.userRepository = userRepository;
        this.menuRepository = menuRepository;
        this.permissionRepository = permissionRepository;
        this.groupRepository = groupRepository;
        this.roleRepository = roleRepository;
    }

    @HandleServiceException
    @Cacheable(value = cacheName, key = "#userId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    public User getUser(long userId) {
        var user = userRepository.getUser(userId);
        if (Objects.isNull(user)) {
            ExceptionUtils.throwServiceException(PanelsErrorCodes.USER_NOT_FOUND, userId);
        }
        return user;
    }


    @HandleServiceException
    public Collection<Menu> getMenusByUser(long userId) {
        var user = getUser(userId);
        if (Objects.nonNull(user) && !user.getMenuIds().isEmpty()) {
            return menuRepository.getMenusByIds(user.getMenuIds());
        }
        return new ArrayList<>();
    }

    @HandleServiceException
    public Collection<Permission> getPermissionsByUser(long userId) {
        var user = getUser(userId);
        if (Objects.nonNull(user) && !user.getPermissionIds().isEmpty()) {
            return permissionRepository.getPermissionsByIds(user.getPermissionIds());
        }
        return new ArrayList<>();
    }

    @HandleServiceException
    public Collection<Group> getGroupsByUser(long userId) {
        var user = getUser(userId);
        if (Objects.nonNull(user) && !user.getGroupIds().isEmpty()) {
            return groupRepository.getGroupsByIds(user.getGroupIds());
        }
        return new ArrayList<>();
    }

    @HandleServiceException
    public Collection<Role> getRolesByUser(long userId) {
        var user = getUser(userId);
        if (Objects.nonNull(user) && !user.getRoleIds().isEmpty()) {
            return roleRepository.getRolesByIds(user.getRoleIds());
        }
        return new ArrayList<>();
    }

    @HandleServiceException
    @CachePut(value = cacheName, key = "#user.userId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    public User createUser(User user, long operatorId) {
        var existedUser = userRepository.getUserByCode(user.getApplicationId(), user.getUserCode());
        if (Objects.nonNull(existedUser)) {
            ExceptionUtils.throwServiceException(PanelsErrorCodes.USER_CODE_EXISTED, user);
        }

        user.createBy(operatorId);
        user.changePassword(encryptUserPassword(defaultPassword));
        return userRepository.saveUser(user);
    }

    @HandleServiceException
    @CachePut(value = cacheName, key = "#user.userId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    public User updateUser(User user, long operatorId) {
        var existedUser = getUser(user.getUserId());
        existedUser = userRepository.getUserByCode(user.getApplicationId(), user.getUserCode());
        if (Objects.nonNull(existedUser) && existedUser.getUserId() != user.getUserId()) {
            ExceptionUtils.throwServiceException(PanelsErrorCodes.USER_CODE_EXISTED, user);
        }
        user.updateBy(operatorId);
        return userRepository.saveUser(user);
    }

    @HandleServiceException
    @CacheEvict(value = cacheName, key = "#userId")
    public boolean enableUser(long userId, long operatorId) {
        var user = getUser(userId);
        if (Objects.isNull(user)) return false;
        // 记录操作日志
        user.enable();
        user.updateBy(operatorId);
        userRepository.saveUser(user);
        return true;
    }

    @HandleServiceException
    @CacheEvict(value = cacheName, key = "#userId")
    public boolean disableUser(long userId, long operatorId) {
        var user = getUser(userId);
        if (Objects.isNull(user)) return false;
        // 记录操作日志
        user.disable();
        user.updateBy(operatorId);
        userRepository.saveUser(user);
        return true;
    }

    @HandleServiceException
    @CacheEvict(value = cacheName, key = "#userId")
    public boolean deleteUser(long userId, long operatorId) {
        var user = getUser(userId);
        if (Objects.isNull(user)) return false;
        // 记录操作日志
        user.delete();
        user.updateBy(operatorId);
        userRepository.saveUser(user);
        return true;
    }

    @HandleServiceException
    @CacheEvict(value = cacheName, key = "#userId")
    public boolean bindUserRole(long userId, long roleId) {
        return userRepository.bindUserRole(userId, roleId);
    }

    @HandleServiceException
    @CacheEvict(value = cacheName, key = "#userId")
    public boolean unbindUserRole(long userId, long roleId) {
        return userRepository.unbindUserRole(userId, roleId);
    }

    @HandleServiceException
    @CacheEvict(value = cacheName, key = "#userId")
    public boolean initUserPassword(long userId, long operatorId) {
        var user = getUser(userId);
        user.changePassword(encryptUserPassword(defaultPassword));
        user.updateBy(operatorId);
        userRepository.saveUser(user);
        return true;
    }

    @HandleServiceException
    @CacheEvict(value = cacheName, key = "#userId")
    public boolean initUserPassword(long userId, String password, long operatorId) {
        var user = getUser(userId);
        user.changePassword(encryptUserPassword(password));
        user.updateBy(operatorId);
        userRepository.saveUser(user);
        return true;
    }

    public String encryptUserPassword(String originalPassword) {
        // return AesUtils.encode(originalPassword, PASSWORD_SECRET_KEY);
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder.encode(originalPassword);
    }

    public boolean validatePassword(String originalPassword, String encryptedPassword) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder.matches(originalPassword, encryptedPassword);
    }
}
